import { useApolloClient } from '@apollo/client';
import { Kind, OperationTypeNode } from 'graphql';
import type { DocumentNode, FieldNode, OperationDefinitionNode } from 'graphql';
import uniq from 'lodash/uniq';

import { imageListQuery } from '../WorkloadCves/Tables/ImageOverviewTable';
import { cveListQuery } from '../WorkloadCves/Tables/WorkloadCVEOverviewTable';
import { deploymentListQuery } from '../WorkloadCves/Tables/DeploymentOverviewTable';
import { deploymentVulnerabilitiesQuery } from '../WorkloadCves/Deployment/DeploymentPageVulnerabilities';
import { imageVulnerabilitiesQuery } from '../WorkloadCves/Image/ImagePageVulnerabilities';
import {
    imageCveAffectedDeploymentsQuery,
    imageCveAffectedImagesQuery,
} from '../WorkloadCves/ImageCve/ImageCvePage';

/**
 * Extracts the top-level field names from a GraphQL document node. This is used to determine
 * which query fields to invalidate.
 *
 * @param documentNode A GraphQL document node
 * @returns An array of top-level field names
 */
function extractTopLevelFieldNames(documentNode: DocumentNode): string[] {
    return documentNode.definitions
        .filter((def): def is OperationDefinitionNode => def.kind === Kind.OPERATION_DEFINITION)
        .filter((def) => def.operation === OperationTypeNode.QUERY)
        .flatMap((def) =>
            def.selectionSet.selections
                .filter((sel): sel is FieldNode => sel.kind === Kind.FIELD)
                .map((sel) => sel.name.value)
        );
}

const queryFieldNames = uniq(
    [
        // Queries for the tables on the Workload CVE list page
        imageListQuery,
        cveListQuery,
        deploymentListQuery,
        // Queries for the Workload CVE detail page
        imageCveAffectedDeploymentsQuery,
        imageCveAffectedImagesQuery,
        // Query for the Workload CVE image detail page
        imageVulnerabilitiesQuery,
        // Query for the Workload CVE deployment detail page
        deploymentVulnerabilitiesQuery,
    ].flatMap(extractTopLevelFieldNames)
);

/**
 * A hook that returns a function that invalidates all queries related to vulnerabilities in Workload CVEs. Active queries will
 * be automatically refetched, while inactive queries will refetch when then become observed again. This should
 * be used any time a mutation is performed that changes the vulnerability state of a CVE. These mutations include:
 * - Deferring a CVE
 * - Marking a CVE as false positive
 * - Approving an exception request
 * - Updating an exception request
 * - Deleting an exception request
 *
 * @returns {invalidateAll} - function that invalidates all queries related to vulnerabilities in Workload CVEs
 */
export default function useInvalidateVulnerabilityQueries() {
    const apolloClient = useApolloClient();

    function invalidateAll() {
        queryFieldNames.forEach((fieldName) => {
            apolloClient.cache.evict({ fieldName });
        });
        apolloClient.cache.gc();
    }

    return {
        invalidateAll,
    };
}
